package spring;

import spring.annotation.MyAutowired;
import spring.annotation.MyComponent;
import spring.annotation.MyComponentScan;
import spring.annotation.MyScope;
import spring.common.MyBeanDefinition;
import spring.common.MyScopeType;
import spring.service.MyBeanNameAware;
import spring.service.MyBeanPostProcessor;
import spring.service.MyInitializingBean;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 自定义Spring容器
 */
public class MyAnnotationConfigApplicationContext {
    /**
     * Bean定义池
     */
    private final AtomicReference<ConcurrentHashMap<String, MyBeanDefinition>> beanDefinitionMap = new AtomicReference<>(new ConcurrentHashMap<>());
    /**
     * 单例Bean池
     */
    private final AtomicReference<ConcurrentHashMap<String, Object>> singletonBeanMap = new AtomicReference<>(new ConcurrentHashMap<>());
    /**
     * Bean处理器池
     */
    private List<MyBeanPostProcessor> myBeanPostProcessors = new ArrayList<>();

    public MyAnnotationConfigApplicationContext(Class<?> configClass) {
        myComponentScanProcess(configClass);
        singletonBeanProcess();

        System.out.println("beanDefinitionMap：" + beanDefinitionMap);
        System.out.println("singletonObjectMap：" + singletonBeanMap);
    }

    /**
     * 扫描注解处理
     */
    private void myComponentScanProcess(Class<?> configClass) {
        //判断是否包含注解
        if (configClass.isAnnotationPresent(MyComponentScan.class)) {
            System.out.println("扫描开始");
            //获取注解
            MyComponentScan myComponentScanAnnotation = configClass.getDeclaredAnnotation(MyComponentScan.class);
            //获取扫描路径
            String[] paths = myComponentScanAnnotation.value();
            for (String path : paths) {
                System.out.println("扫描路径：" + path);
                path = path.replace(".", "/");

                /*
                  类加载器
                  Bootstrap：jre/lib
                  Ext：jre/ext/lib
                  App：classpath
                 */
                //获取类加载器
                ClassLoader classLoader = MyAnnotationConfigApplicationContext.class.getClassLoader();
                //获取扫描文件Url
                URL resourceUrl = classLoader.getResource(path);
                System.out.println("扫描文件Url：" + resourceUrl);
                if (resourceUrl == null) {
                    throw new RuntimeException("无法获取扫描文件Url");
                }

                File resourceFile = new File(resourceUrl.getFile());
                scanFile(resourceFile, classLoader);
            }

            System.out.println("扫描结束");
        }
    }

    /**
     * 扫描文件
     */
    private void scanFile(File file, ClassLoader classLoader) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null) {
                for (File childrenFile : files) {
                    scanFile(childrenFile, classLoader);
                }
            }
        } else {
            String fileName = file.getAbsolutePath();
            System.out.println("fileName：" + fileName);
            if (fileName.endsWith(".class")) {
                String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
                className = className.replace("\\", ".");
                System.out.println("className：" + className);

                try {
                    //获取类
                    Class<?> clazz = classLoader.loadClass(className);
                    myComponentProcess(clazz);
                } catch (ClassNotFoundException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    /**
     * Bean注解处理
     */
    private void myComponentProcess(Class<?> clazz) {
        //判断是否包含注解
        if (clazz.isAnnotationPresent(MyComponent.class)) {
            //获取注解
            MyComponent myComponentAnnotation = clazz.getDeclaredAnnotation(MyComponent.class);
            String beanName = myComponentAnnotation.value();

            //Bean类型注解处理
            //判断是否包含注解
            String scope = "";
            if (clazz.isAnnotationPresent(MyScope.class)) {
                MyScope myScopeAnnotation = clazz.getDeclaredAnnotation(MyScope.class);
                scope = myScopeAnnotation.value();
            }
            else {
                scope = MyScopeType.SINGLETON;
            }

            MyBeanDefinition myBeanDefinition = new MyBeanDefinition(clazz, scope);
            beanDefinitionMap.get().put(beanName, myBeanDefinition);
        }
    }

    /**
     * 单例Bean处理
     */
    private void singletonBeanProcess() {
        List<String> beanPostProcessorBeanNames = new ArrayList<>();
        List<String> otherBeanNames = new ArrayList<>();

        for (String beanName : beanDefinitionMap.get().keySet()){
            MyBeanDefinition myBeanDefinition = beanDefinitionMap.get().get(beanName);
            if (Objects.equals(MyScopeType.SINGLETON, myBeanDefinition.getScope())) {
                if (isBeanPostProcessor(myBeanDefinition.getClazz())) {
                    beanPostProcessorBeanNames.add(beanName);
                } else {
                    otherBeanNames.add(beanName);
                }
            }
        }

        for (String beanName : beanPostProcessorBeanNames) {
            Object bean = singletonBeanCreate(beanName);
            myBeanPostProcessors.add((MyBeanPostProcessor) bean);
        }

        for (String beanName : otherBeanNames) {
            singletonBeanCreate(beanName);
        }
    }

    /**
     * 是否实现Bean处理器接口
     */
    private boolean isBeanPostProcessor(Class<?> clazz) {
        return MyBeanPostProcessor.class.isAssignableFrom(clazz);
    }

    /**
     * 单例Bean创建
     */
    private Object singletonBeanCreate(String beanName) {
        if (singletonBeanMap.get().containsKey(beanName)) {
            return singletonBeanMap.get().get(beanName);
        }

        MyBeanDefinition myBeanDefinition = beanDefinitionMap.get().get(beanName);
        Object bean = createBean(beanName, myBeanDefinition);
        singletonBeanMap.get().put(beanName, bean);
        return bean;
    }

    /**
     * 创建Bean
     */
    private Object createBean(String beanName, MyBeanDefinition myBeanDefinition) {
        System.out.println("创建Bean：" + beanName);
        Class<?> clazz = myBeanDefinition.getClazz();
        try {
            //创建对象
            Object object = clazz.getDeclaredConstructor().newInstance();
            myAutowiredProcess(object, clazz);
            awareProcess(object, beanName);
            object = postProcessBeforeInitializationProcess(object, beanName);
            initializingBeanProcess(object);
            object = postProcessAfterInitializationProcess(object, beanName);
            return object;
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 依赖注入注解处理
     */
    private void myAutowiredProcess(Object object, Class<?> clazz) {
        for (Field field : clazz.getDeclaredFields()) {
            //判断是否包含注解
            if (field.isAnnotationPresent(MyAutowired.class)) {
                Object bean = getBean(field.getName(), true);
                try {
                    field.setAccessible(true);
                    field.set(object, bean);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    /**
     * 获取Bean
     */
    private Object getBean(String beanName, boolean isCreate) {
        if (beanDefinitionMap.get().containsKey(beanName)) {
            MyBeanDefinition myBeanDefinition = beanDefinitionMap.get().get(beanName);
            if (Objects.equals(MyScopeType.SINGLETON, myBeanDefinition.getScope())) {
                if (singletonBeanMap.get().containsKey(beanName)) {
                    return singletonBeanMap.get().get(beanName);
                } else if (isCreate) {
                    Object object = createBean(beanName, myBeanDefinition);
                    singletonBeanMap.get().put(beanName, object);
                    return object;
                } else {
                    throw new RuntimeException("不存在对应的bean");
                }
            }
            else{
                return createBean(beanName, myBeanDefinition);
            }
        }
        else {
            throw new RuntimeException("不存在对应的bean");
        }
    }

    /**
     * Aware回调处理
     */
    private void awareProcess(Object object, String beanName) {
        if (object instanceof MyBeanNameAware) {
            ((MyBeanNameAware) object).setBeanName(beanName);
        }
    }

    /**
     * Bean初始化前处理
     */
    private Object postProcessBeforeInitializationProcess(Object object, String beanName) {
        for (MyBeanPostProcessor myBeanPostProcessor : myBeanPostProcessors){
            Object result = myBeanPostProcessor.postProcessBeforeInitialization(object, beanName);
            if (result != null) {
                return result;
            }
        }
        return object;
    }

    /**
     * 初始化Bean处理
     */
    private void initializingBeanProcess(Object object) {
        if (object instanceof MyInitializingBean) {
            try {
                ((MyInitializingBean) object).afterPropertiesSet();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * Bean初始化后处理
     */
    private Object postProcessAfterInitializationProcess(Object object, String beanName) {
        for (MyBeanPostProcessor myBeanPostProcessor : myBeanPostProcessors){
            Object postResult = myBeanPostProcessor.postProcessAfterInitialization(object, beanName);
            Object aopResult = aopProcess(myBeanPostProcessor, postResult, beanName);
            if (aopResult != null) {
                return aopResult;
            } else if (postResult != null) {
                return postResult;
            }
        }
        return object;
    }

    /**
     * AOP处理
     */
    private Object aopProcess(MyBeanPostProcessor myBeanPostProcessor, Object object, String beanName) {
        Object result = myBeanPostProcessor.aopProcess(object, beanName);
        if (result != null) {
            return result;
        }
        return object;
    }

    /**
     * 获取Bean，如果是单例Bean，就从单例Bean池获取，不是则每次创建新的Bean
     */
    public Object getBean(String beanName) {
        return getBean(beanName, false);
    }
}